package org.irri.smta.service.impl;

import java.util.Date;
import java.util.List;

import javax.inject.Inject;
import javax.inject.Named;

import org.irri.smta.domain.Material;
import org.irri.smta.repository.IMaterialRepository;
import org.irri.smta.service.IMaterialService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.transaction.annotation.Transactional;

@Named("materialService")
@Transactional(readOnly = true)
public class MaterialService implements
		IMaterialService<Material, Long> {

	private static final Logger LOGGER = LoggerFactory
			.getLogger(MaterialService.class);

	@Inject
	@Named("materialRepository")
	private IMaterialRepository materialRepository;
	
	@Transactional(readOnly = true)
	@Override
	public Long getRowCount() {
		LOGGER.debug("Long getRowCount()");
		Long rowCount = materialRepository.count();
		LOGGER.debug("rowCount = " + rowCount);
		return rowCount;
	}

	@Transactional(readOnly = false, rollbackFor = Exception.class)
	@Override
	public Material create(Material material) {
		LOGGER.debug("Material create(Material material):"
				+ material);
		Date now = new Date();
		material.setCreatedDate(now);
		material.setUpdatedDate(now);
		LOGGER.debug("Creating a new material with information: "
				+ material);
		return materialRepository.save(material);
	}

	@Transactional(readOnly = false, rollbackFor = Exception.class)
	@Override
	public Material update(Material material) throws Exception {
		LOGGER.debug("Material update(Material material): "
				+ material);
		material.setUpdatedDate(new Date());
		LOGGER.debug("Updating material with information: " + material);

		Material materialDb = materialRepository
				.findOne(material.getId());

		if (materialDb == null) {
			LOGGER.debug("No material found with id: "
					+ material.getId());
			throw new Exception();
		}

		materialRepository.save(material);
		return material;
	}

	@Transactional(readOnly = false, rollbackFor = Exception.class)
	@Override
	public Material delete(Long id) throws Exception {
		LOGGER.debug("Material delete(Long id): " + id);

		Material material = materialRepository.findOne(id);

		if (material == null) {
			LOGGER.debug("No material found with id: " + id);
			throw new Exception();
		}

		materialRepository.delete(material);
		return material;
	}

	@Transactional(readOnly = true)
	@Override
	public Material findById(Long id) {
		LOGGER.debug("findById(Long id): " + id);
		return materialRepository.findOne(id);
	}

	@Transactional(readOnly = true)
	@Override
	public List<Material> findAll() {
		LOGGER.debug("findAll()");
		List<Material> materialList = materialRepository.findAll();
		return postProcess(materialList);
	}

	@Transactional(readOnly = true)
	@Override
	public List<Material> findAllOrdered() {
		LOGGER.debug("findAllOrdered()");
		List<Material> materialList = materialRepository.findAllOrdered();
		return postProcess(materialList);
	}

	@Transactional(readOnly = true)
	@Override
	public List<Material> findByPage(Integer pageIndex, Integer pageSize) {
		Page<Material> requestedPage = materialRepository
				.findAll(constructPageSpecification(pageIndex, pageSize));
		List<Material> materialList = requestedPage.getContent();
		return postProcess(materialList);
	}

	@Transactional(readOnly = true)
	@Override
	public List<Material> findAll(Pageable pageable) {
		LOGGER.debug("List<Material> findAll(Pageable pageable)");
		Page<Material> page = materialRepository.findAll(pageable);
		List<Material> materialList = page.getContent();
		return postProcess(materialList);
	}

	@Transactional(readOnly = true)
	@Override
	public List<Material> findAll(
			Specification<Material> specification, Pageable pageable) {
		LOGGER.debug("List<Material> findAll(Specification<Material> specification, Pageable pageable)");
		Page<Material> page = materialRepository.findAll(specification,
				pageable);
		List<Material> materialList = page.getContent();
		return postProcess(materialList);
	}

	/**
	 * Returns a new object which specifies the the wanted result page.
	 * 
	 * @param pageIndex The index of the wanted result page
	 * @return
	 */
	private Pageable constructPageSpecification(Integer pageIndex,
			Integer pageSize) {
		Pageable pageSpecification = new PageRequest(pageIndex, pageSize,
				sortByIdAsc());
		return pageSpecification;
	}

	/**
	 * Returns a Sort object which sorts in ascending order
	 * 
	 * @return
	 */
	private Sort sortByIdAsc() {
		return new Sort(Sort.Direction.ASC, "id");
	}

	private List<Material> postProcess(List<Material> materialList) {
		for (Material material : materialList) {
			postProcess(material);
		}
		return materialList;
	}

	private Material postProcess(Material material) {
		return material;
	}

}
